package com.allwees.bs.module.core.wf;

import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.allwees.bs.core.modelbase.exception.BusinessException;
import com.allwees.bs.module.core.wf.dao.WorkflowInstanceDao;
import com.allwees.bs.module.core.wf.dao.WorkflowLogDao;
import com.allwees.bs.module.core.wf.entity.WorkflowInstance;
import com.allwees.bs.module.core.wf.entity.WorkflowLog;
import com.allwees.core.common.constant.EStatus;
import com.allwees.core.common.exception.BaseException;
import com.allwees.core.common.util.BeanUtil;
import com.allwees.core.common.util.EnumUtil;
import com.allwees.core.wf.Action;
import com.allwees.core.wf.FsmState;
import com.allwees.core.wf.WfStateException;
import com.allwees.core.wf.WorkflowModel;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;

/**
 * 关联工作流的业务对象
 *
 * @author yangylsky
 * @since 2019-12-12
 *
 * @param
 */
@SuppressWarnings({"unchecked", "rawtypes"})
public interface WorkflowAware extends WorkflowModel {
	@JsonIgnore
    @JSONField(serialize = false)
	default <S extends FsmState> Class<S> getWfClass() {
		try {
			return (Class<S>) Class.forName(getWfName());
		} catch(ClassNotFoundException e) {
			throw new BaseException("FsmState[" + getWfName() + "]不存在");
		}
	}

	default <S extends FsmState> void setWf(S wf) {
		setWfName(((Enum) wf).getDeclaringClass().getName());
	}

	@JsonIgnore
	@JSONField(serialize = false)
	default <S extends FsmState> S getState() {
		if(StringUtils.isBlank(getWfName()) || StringUtils.isBlank(getWfState())) {
			return null;
		}
		return (S) EnumUtil.getEnum(getWfName(), getWfState());
	}

	default <S extends FsmState> void setState(S state) {
		setWfState(state.name());
	}

	default <S extends FsmState> void assertState(S state) {
		S actualState = getState();
		Assert.isTrue(actualState != null && actualState.equals(state), "assert failed, expected:[" + state + "], actual:[" + actualState + "]");
	}

	/**
	 * 设置Entity状态
	 *
	 * @param status
	 * @return
	 */
	void setEStatus(EStatus status);

	/**
	 * 启动一个新流程
	 *
	 * @param state
	 * @return
	 */
	default <S extends FsmState> S start(S state) {
		return start(state, null);
	}

	/**
	 * 启动一个新流程
	 *
	 * @param state
	 * @return
	 */
	default <S extends FsmState, A extends Action> S start(S state, A action) {
		return start(state, action, null);
	}

	/**
	 * 启动一个新流程
	 *
	 * @param state
	 * @param operatorUuid
	 * @return
	 */
	default <S extends FsmState, A extends Action> S start(S state, A action, String operatorUuid) {
//		if(StringUtils.isNotBlank(getWfUuid())) {
//			throw new WfStateException("当前流程已经启动，无需再次启动");
//		}
		Class stateClass = ((Enum) state).getDeclaringClass();
		if(!state.isStartable()) {
			throw new WfStateException("节点[" + stateClass.getSimpleName() + ": " + state.name() + "]不是可启动的节点");
		}
		WorkflowInstance wfInstance = new WorkflowInstance();
		wfInstance.setStateClassName(stateClass.getName());
		if(StringUtils.isBlank(wfInstance.getActionClassName())) {
			wfInstance.setActionClassName(action.getClass().getName());
		}
		wfInstance.setBsKey(getBsKey());
		wfInstance.setState(state);
		S nextState = (S) wfInstance.start(action);
		WorkflowInstanceDao wfInstanceDao = BeanUtil.getBean(WorkflowInstanceDao.class);
		if(wfInstanceDao != null) {
			wfInstanceDao.create(wfInstance);
		}
		setWfUuid(wfInstance.getUuid());
		setWf(state);
		setState(nextState);

		_updateEStatus(action);
		saveWorkflowLog(wfInstance, operatorUuid, null, "启动流程", null);
		return nextState;
	}

	/**
	 * 触发流程变迁
	 *
	 * @param action
	 * @return
	 */
	default <S extends FsmState, A extends Action> S signal(A action) {
		return signal(action, null, null, null, null);
	}

	/**
	 * 触发流程变迁，并传入记录参数
	 *
	 * @param action
	 * @param reason
	 * @param remark
	 * @param photos
	 * @return
	 */
	default <S extends FsmState, A extends Action> S signal(A action, String reason, String remark, String[] photos) {
		return signal(action, null, reason, remark, photos);
	}

	default <S extends FsmState, A extends Action> S signal(A action, String operatorUuid) {
		return signal(action, operatorUuid, null, null, null);
	}

	default <S extends FsmState, A extends Action> S signal(A action, String operatorUuid, String remark) {
		return signal(action, operatorUuid, null, remark, null);
	}

	/**
	 * 触发流程变迁，并传入记录参数
	 *
	 * @param action
	 * @param operatorUuid
	 * @param reason
	 * @param remark
	 * @param photos
	 * @return
	 */
	default <S extends FsmState, A extends Action> S signal(A action, String operatorUuid, String reason, String remark, String[] photos) {
		if(StringUtils.isBlank(getWfUuid())) {
			throw new WfStateException("当前流程还未启动，请先启动流程");
		}
		WorkflowInstanceDao wfInstanceDao =BeanUtil.getBean(WorkflowInstanceDao.class);
		WorkflowInstance wfInstance = wfInstanceDao.findByUuid(getWfUuid());
		if(wfInstance == null) {
			throw new BusinessException("流程[" + getWfUuid() + "]不存在");
		}
		if(StringUtils.isBlank(wfInstance.getBsKey())) {
			wfInstance.setBsKey(getBsKey());
		}
		S state = (S) wfInstance.signal(action);
		_updateEStatus(action);
		wfInstanceDao.updateByUuid(wfInstance, wfInstance.getUuid());
		setState(state);
		saveWorkflowLog(wfInstance, operatorUuid, reason, remark, photos);
		return state;
	}

	/**
	 * 根据Action更新当前WorkflowAware的status
	 *
	 * @param action
	 */
	default void _updateEStatus(Action action) {
		if(action.afterActionStatus() != null) {
			setEStatus(action.afterActionStatus());
		} else if (getState().ended()) {
			setEStatus(EStatus.COMPLETED);
		}
	}

	/**
	 * 记录工作流变迁记录（此方法仅供内部调用）
	 *
	 * @param wfInstance
	 * @param operatorUuid
	 * @param reason
	 * @param remark
	 * @param photos
	 */
	default void saveWorkflowLog(WorkflowInstance wfInstance, String operatorUuid, String reason, String remark, String[] photos) {
		WorkflowLog wfLog = new WorkflowLog();
		wfLog.setWorkflowUuid(wfInstance.getUuid());
		wfLog.setWfAction(wfInstance.getWfLatestAction());
		wfLog.setWfBackState(wfInstance.getWfBackState());
		wfLog.setWfState(wfInstance.getWfState());
		wfLog.setAfterActionStatus(wfInstance.getLatestAction().afterActionStatus());
		wfLog.setOperatorUuid(operatorUuid);
		wfLog.setReason(reason);
		wfLog.setRemark(remark);
		wfLog.setPhotos(photos);
		WorkflowLogDao wfLogDao = BeanUtil.getBean(WorkflowLogDao.class);
		if(wfLogDao != null) {
			wfLogDao.create(wfLog);
		}
	}
}
